home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / hplip / ui4 / sendfaxdialog.py < prev    next >
Encoding:
Python Source  |  2009-04-14  |  35.3 KB  |  1,027 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # (c) Copyright 2001-2009 Hewlett-Packard Development Company, L.P.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18. #
  19. # Authors: Don Welch
  20. #
  21.  
  22. # StdLib
  23. import operator
  24. import struct
  25. import Queue
  26.  
  27. # Local
  28. from base.g import *
  29. from base import device, utils, pml
  30. from prnt import cups
  31. from base.codes import *
  32. from ui_utils import *
  33.  
  34. # Qt
  35. from PyQt4.QtCore import *
  36. from PyQt4.QtGui import *
  37.  
  38. # Ui
  39. from sendfaxdialog_base import Ui_Dialog
  40. from filetable import FileTable, FILETABLE_TYPE_FAX
  41. from printernamecombobox import PrinterNameComboBox, PRINTERNAMECOMBOBOX_TYPE_FAX_ONLY
  42. from printsettingsdialog import PrintSettingsDialog
  43. from faxsetupdialog import FaxSetupDialog
  44.  
  45.  
  46. PAGE_SELECT_FAX = 0
  47. PAGE_COVERPAGE = 1
  48. PAGE_FILES = 2
  49. PAGE_RECIPIENTS = 3
  50. PAGE_SEND_FAX = 4
  51. PAGE_MAX = 4
  52.  
  53. STATUS_INFORMATION = 0
  54. STATUS_WARNING = 1
  55. STATUS_ERROR = 2
  56.  
  57. MIME_TYPE_COVERPAGE = "application/hplip-fax-coverpage"
  58.  
  59. fax_enabled = prop.fax_build
  60.  
  61. if fax_enabled:
  62.     try:
  63.         from fax import fax
  64.     except ImportError:
  65.         # This can fail on Python < 2.3 due to the datetime module
  66.         # or if fax was diabled during the build
  67.         fax_enabled = False
  68.  
  69. if not fax_enabled:
  70.     log.error("Fax disabled.")
  71.  
  72. coverpages_enabled = False
  73. if fax_enabled:
  74.     try:
  75.         import reportlab
  76.         ver = reportlab.Version
  77.         try:
  78.             ver_f = float(ver)
  79.         except ValueError:
  80.             ver_f = 0.0
  81.  
  82.         if ver_f >= 2.0:
  83.             coverpages_enabled = True
  84.         else:
  85.             log.warn("Pre-2.0 version of Reportlab installed. Fax coverpages disabled.")
  86.  
  87.     except ImportError:
  88.         log.warn("Reportlab not installed. Fax coverpages disabled.")
  89.  
  90. if not coverpages_enabled:
  91.     log.warn("Please install version 2.0+ of Reportlab for coverpage support.")
  92.  
  93. if fax_enabled:
  94.     from fabwindow import FABWindow
  95.  
  96. if coverpages_enabled:
  97.     from fax import coverpages
  98.  
  99.  
  100. class SendFaxDialog(QDialog, Ui_Dialog):
  101.     def __init__(self, parent, printer_name, device_uri=None, args=None):
  102.         QDialog.__init__(self, parent)
  103.         self.setupUi(self)
  104.  
  105.         self.printer_name = printer_name
  106.         if device_uri is not None:
  107.             self.device_uri = device_uri
  108.         else:
  109.             self.device_uri = device.getDeviceURIByPrinterName(self.printer_name)
  110.  
  111.         self.args = args
  112.         self.dev = None
  113.  
  114.         self.dbus_avail, self.service, session_bus = device.init_dbus()
  115.  
  116.         self.CheckTimer = None
  117.         self.lock_file = None
  118.         self.file_list = []
  119.         self.recipient_list = []
  120.  
  121.         self.initUi()
  122.  
  123.         if self.printer_name:
  124.             if coverpages_enabled:
  125.                 QTimer.singleShot(0, self.displayCoverpagePage)
  126.             else:
  127.                 self.lockAndLoad()
  128.                 QTimer.singleShot(0, self.displayFilesPage)
  129.         else:
  130.             QTimer.singleShot(0, self.displaySelectFaxPage)
  131.  
  132.  
  133.     def initUi(self):
  134.         # connect signals/slots
  135.         self.connect(self.CancelButton, SIGNAL("clicked()"), self.CancelButton_clicked)
  136.         self.connect(self.BackButton, SIGNAL("clicked()"), self.BackButton_clicked)
  137.         self.connect(self.NextButton, SIGNAL("clicked()"), self.NextButton_clicked)
  138.  
  139.         self.initSelectFaxPage()
  140.         self.initCoverpagePage()
  141.         self.initFilesPage()
  142.         self.initRecipientsPage()
  143.         self.initSendFaxPage()
  144.  
  145.         # Application icon
  146.         self.setWindowIcon(QIcon(load_pixmap('prog', '48x48')))
  147.  
  148.  
  149.     def lockAndLoad(self):
  150.         # Start up check timer here, since the fax name is now known
  151.         if self.CheckTimer is None:
  152.             self.CheckTimer = QTimer(self)
  153.             self.connect(self.CheckTimer, SIGNAL("timeout()"), self.CheckTimer_timeout)
  154.             self.CheckTimer.start(3000)
  155.  
  156.         # Lock the app
  157.         if self.printer_name and self.lock_file is None:
  158.             ok, self.lock_file = utils.lock_app('hp-sendfax-%s' % self.printer_name, True)
  159.  
  160.             if not ok:
  161.                 log.error("hp-sendfax is already running for fax %s" % self.printer_name)
  162.                 # TODO:
  163.  
  164.     #
  165.     # Select Fax Page
  166.     #
  167.  
  168.     def initSelectFaxPage(self):
  169.         self.FaxComboBox.setType(PRINTERNAMECOMBOBOX_TYPE_FAX_ONLY)
  170.         self.connect(self.FaxComboBox, SIGNAL("PrinterNameComboBox_currentChanged"), self.FaxComboBox_currentChanged)
  171.         self.connect(self.FaxComboBox, SIGNAL("PrinterNameComboBox_noPrinters"), self.FaxComboBox_noPrinters)
  172.         self.connect(self.FaxOptionsButton, SIGNAL("clicked()"), self.FaxOptionsButton_clicked)
  173.         self.connect(self.FaxSetupButton, SIGNAL("clicked()"), self.FaxSetupButton_clicked)
  174.  
  175.         if self.printer_name is not None:
  176.             self.FaxComboBox.setInitialPrinter(self.printer_name)
  177.  
  178.  
  179.     def displaySelectFaxPage(self):
  180.         self.BackButton.setEnabled(False)
  181.         self.updateStepText(PAGE_SELECT_FAX)
  182.  
  183.         if not fax_enabled:
  184.             FailureUI(self, self.__tr("<b>PC send fax support is not enabled.</b><p>Re-install HPLIP with fax support or use the device front panel to send a fax.</p><p>Click <i>OK</i> to exit.</p>"))
  185.             self.close()
  186.             return
  187.  
  188.         if not self.dbus_avail:
  189.             FailureUI(self, self.__tr("<b>PC send fax support requires DBus and hp-systray.</b><p>Please check the HPLIP installation for proper installation of DBus and hp-systray support.</p><p>Click <i>OK</i> to exit.</p>"))
  190.             self.close()
  191.             return
  192.  
  193.         self.FaxComboBox.updateUi()
  194.  
  195.  
  196.     def FaxComboBox_currentChanged(self, device_uri, printer_name):
  197.         self.printer_name = printer_name
  198.         self.device_uri = device_uri
  199.  
  200.  
  201.     def FaxComboBox_noPrinters(self):
  202.         FailureUI(self, self
  203.                   .__tr("<b>No installed fax devices found.</b><p>Please setup a fax device and try again (try using 'hp-setup').</p><p>Click <i>OK</i> to exit.</p>"))
  204.         self.close()
  205.  
  206.  
  207.     def FaxOptionsButton_clicked(self):
  208.         dlg = PrintSettingsDialog(self, self.printer_name, fax_mode=True)
  209.         dlg.exec_()
  210.  
  211.  
  212.     def FaxSetupButton_clicked(self):
  213.         dlg = FaxSetupDialog(self, self.device_uri)
  214.         dlg.exec_()
  215.  
  216.     #
  217.     # Coverpage Page
  218.     #
  219.  
  220.     def initCoverpagePage(self):
  221.         self.cover_page_message = ''
  222.         self.cover_page_re = ''
  223.         self.preserve_formatting = False
  224.         self.cover_page_func, cover_page_png = None, None
  225.         self.last_job_id = 0
  226.         self.busy = False
  227.         self.PrevCoverPageButton.setIcon(QIcon(load_pixmap("prev", "16x16")))
  228.         self.NextCoverPageButton.setIcon(QIcon(load_pixmap("next", "16x16")))
  229.  
  230.         if coverpages_enabled:
  231.             self.cover_page_list = coverpages.COVERPAGES.keys()
  232.             self.cover_page_index = self.cover_page_list.index("basic")
  233.             self.cover_page_max = len(self.cover_page_list)-1
  234.             self.cover_page_name = self.cover_page_list[self.cover_page_index]
  235.  
  236.             self.connect(self.PrevCoverPageButton, SIGNAL("clicked()"), self.PrevCoverPageButton_clicked)
  237.             self.connect(self.NextCoverPageButton, SIGNAL("clicked()"), self.NextCoverPageButton_clicked)
  238.             self.connect(self.CoverPageGroupBox, SIGNAL("toggled(bool)"), self.CoverPageGroupBox_toggled)
  239.             self.connect(self.MessageEdit, SIGNAL("textChanged()"), self.MessageEdit_textChanged)
  240.             self.connect(self.RegardingEdit, SIGNAL("textChanged(const QString &)"), self.RegardingEdit_textChanged)
  241.             self.connect(self.PreserveFormattingCheckBox, SIGNAL("toggled(bool)"),
  242.                         self.PreserveFormattingCheckBox_toggled)
  243.         else:
  244.             self.CoverPageGroupBox.setEnabled(False)
  245.  
  246.  
  247.     def displayCoverpagePage(self):
  248.         self.BackButton.setEnabled(False) # No going back once printer is chosen
  249.         self.NextButton.setEnabled(True)
  250.  
  251.         self.lockAndLoad()
  252.  
  253.         self.updateCoverpageButtons()
  254.         self.displayCoverpagePreview()
  255.         self.displayPage(PAGE_COVERPAGE)
  256.  
  257.  
  258.     def MessageEdit_textChanged(self):
  259.         self.cover_page_message = unicode(self.MessageEdit.toPlainText())
  260.  
  261.  
  262.     def RegardingEdit_textChanged(self, t):
  263.         self.cover_page_re = unicode(t)
  264.  
  265.  
  266.     def PreserveFormattingCheckBox_toggled(self, b):
  267.         self.preserve_formatting = b
  268.  
  269.  
  270.     def PrevCoverPageButton_clicked(self):
  271.         self.cover_page_index -= 1
  272.         if self.cover_page_index < 0:
  273.             self.cover_page_index = 0
  274.         else:
  275.             self.updateCoverpageButtons()
  276.             self.displayCoverpagePage()
  277.  
  278.  
  279.     def NextCoverPageButton_clicked(self):
  280.         self.cover_page_index += 1
  281.         if self.cover_page_index > self.cover_page_max:
  282.             self.cover_page_index = self.cover_page_max
  283.         else:
  284.             self.updateCoverpageButtons()
  285.             self.displayCoverpagePage()
  286.  
  287.  
  288.     def displayCoverpagePreview(self):
  289.         self.cover_page_name = self.cover_page_list[self.cover_page_index]
  290.         self.cover_page_func = coverpages.COVERPAGES[self.cover_page_name][0]
  291.         self.CoverPageName.setText(QString('<i>"%1"</i>').arg(self.cover_page_name))
  292.         self.CoverPagePreview.setPixmap(load_pixmap(coverpages.COVERPAGES[self.cover_page_name][1], 'other'))
  293.  
  294.         if self.CoverPageGroupBox.isChecked():
  295.             self.addCoverPage()
  296.         else:
  297.             self.removeCoverPage()
  298.  
  299.  
  300.     def updateCoverpageButtons(self):
  301.         enabled = self.CoverPageGroupBox.isChecked()
  302.         self.PrevCoverPageButton.setEnabled(enabled and self.cover_page_index != 0)
  303.         self.NextCoverPageButton.setEnabled(enabled and self.cover_page_index != self.cover_page_max)
  304.  
  305.  
  306.     def CoverPageGroupBox_toggled(self, b):
  307.         self.updateCoverpageButtons()
  308.         if b:
  309.             self.addCoverPage()
  310.         else:
  311.             self.removeCoverPage()
  312.  
  313.  
  314.     def addCoverPage(self):
  315.         self.removeCoverPage()
  316.         self.FilesTable.addFile(self.cover_page_name, MIME_TYPE_COVERPAGE,
  317.                                 self.__tr('HP Fax Coverpage: "%1"').arg(self.cover_page_name),
  318.                                 self.__tr("Cover Page"), 1)
  319.  
  320.  
  321.     def removeCoverPage(self):
  322.         self.FilesTable.removeFileByMIMEType(MIME_TYPE_COVERPAGE)
  323.  
  324.  
  325.     def toggleCoverPage(self, b):
  326.         self.disconnect(self.CoverPageGroupBox, SIGNAL("toggled(bool)"), self.CoverPageGroupBox_toggled)
  327.         self.CoverPageGroupBox.setChecked(b)
  328.         self.connect(self.CoverPageGroupBox, SIGNAL("toggled(bool)"), self.CoverPageGroupBox_toggled)
  329.  
  330.  
  331.     #
  332.     # Files Page
  333.     #
  334.  
  335.     def initFilesPage(self):
  336.         self.FilesTable.setType(FILETABLE_TYPE_FAX)
  337.         self.FilesTable.setFaxCallback(self.FileTable_callback)
  338.         self.connect(self.FilesTable, SIGNAL("isEmpty"), self.FilesTable_isEmpty)
  339.         self.connect(self.FilesTable, SIGNAL("isNotEmpty"), self.FilesTable_isNotEmpty)
  340.         self.connect(self.FilesTable, SIGNAL("fileListChanged"), self.FilesTable_fileListChanged)
  341.  
  342.  
  343.     def displayFilesPage(self):
  344.         self.FilesTable.updateUi(False)
  345.  
  346.         if self.args is not None:
  347.             for a in self.args:
  348.                 f = os.path.abspath(os.path.expanduser(a))
  349.                 if os.path.exists(f) and os.access(f, os.R_OK):
  350.                     self.renderFile(f)
  351.  
  352.             self.args = None
  353.  
  354.         self.restoreNextButton()
  355.         self.NextButton.setEnabled(self.FilesTable.isNotEmpty())
  356.         self.BackButton.setEnabled(coverpages_enabled)
  357.         self.FilesPageNote.setText(self.__tr("Note: You may also add files to the fax by printing from any application to the '%1' fax printer.").arg(self.printer_name))
  358.         self.displayPage(PAGE_FILES)
  359.  
  360.  
  361.     def FilesTable_isEmpty(self):
  362.         self.NextButton.setEnabled(False)
  363.  
  364.  
  365.     def FilesTable_isNotEmpty(self):
  366.         self.NextButton.setEnabled(True)
  367.  
  368.  
  369.     def FilesTable_fileListChanged(self):
  370.         self.file_list = self.FilesTable.file_list
  371.         self.toggleCoverPage(self.FilesTable.isMIMETypeInList(MIME_TYPE_COVERPAGE))
  372.  
  373.  
  374.     #
  375.     # Recipients Page
  376.     #
  377.  
  378.     def initRecipientsPage(self):
  379.         # setup validators
  380.         self.QuickAddFaxEdit.setValidator(PhoneNumValidator(self.QuickAddFaxEdit))
  381.  
  382.         # Fax address book database
  383.         self.db = fax.FaxAddressBook()
  384.  
  385.         # Fax address book window
  386.         self.fab = FABWindow(self)
  387.         self.fab.setWindowFlags(Qt.Tool) # Keeps the Fab window on top
  388.  
  389.         self.connect(self.fab, SIGNAL("databaseChanged"), self.FABWindow_databaseChanged)
  390.  
  391.         # connect signals
  392.         self.connect(self.QuickAddFaxEdit, SIGNAL("textChanged(const QString &)"),
  393.                     self.QuickAddFaxEdit_textChanged)
  394.         self.connect(self.QuickAddNameEdit, SIGNAL("textChanged(const QString &)"),
  395.                     self.QuickAddNameEdit_textChanged)
  396.         self.connect(self.QuickAddButton, SIGNAL("clicked()"), self.QuickAddButton_clicked)
  397.         self.connect(self.FABButton, SIGNAL("clicked()"), self.FABButton_clicked)
  398.         self.connect(self.AddIndividualButton, SIGNAL("clicked()"), self.AddIndividualButton_clicked)
  399.         self.connect(self.AddGroupButton, SIGNAL("clicked()"), self.AddGroupButton_clicked)
  400.         self.connect(self.RemoveRecipientButton, SIGNAL("clicked()"), self.RemoveRecipientButton_clicked)
  401.         self.connect(self.MoveRecipientUpButton, SIGNAL("clicked()"), self.MoveRecipientUpButton_clicked)
  402.         self.connect(self.MoveRecipientDownButton, SIGNAL("clicked()"), self.MoveRecipientDownButton_clicked)
  403.         self.connect(self.RecipientsTable, SIGNAL("itemSelectionChanged()"),
  404.                     self.RecipientsTable_itemSelectionChanged)
  405.         self.connect(self.RecipientsTable, SIGNAL("itemDoubleClicked(QTableWidgetItem *)"),
  406.                     self.RecipientsTable_itemDoubleClicked)
  407.  
  408.         # setup icons
  409.         self.FABButton.setIcon(QIcon(load_pixmap("fab", "16x16")))
  410.         self.AddIndividualButton.setIcon(QIcon(load_pixmap("add_user", "16x16")))
  411.         self.AddGroupButton.setIcon(QIcon(load_pixmap("add_users", "16x16")))
  412.         self.RemoveRecipientButton.setIcon(QIcon(load_pixmap("remove_user", "16x16")))
  413.         self.MoveRecipientUpButton.setIcon(QIcon(load_pixmap("up_user", "16x16")))
  414.         self.MoveRecipientDownButton.setIcon(QIcon(load_pixmap("down_user", "16x16")))
  415.         self.QuickAddButton.setIcon(QIcon(load_pixmap("add_user_quick", "16x16")))
  416.  
  417.         # setup initial state
  418.         self.QuickAddButton.setEnabled(False)
  419.  
  420.         self.recipient_headers = [self.__tr("Name"), self.__tr("Fax number"), self.__tr("Notes")]
  421.  
  422.  
  423.     def FABWindow_databaseChanged(self, action, s1='', s2=''):
  424.         self.db.load()
  425.  
  426.         if action in (FAB_NAME_ADD, FAB_GROUP_ADD, FAB_GROUP_RENAME,
  427.                       FAB_GROUP_REMOVE, FAB_GROUP_MEMBERSHIP_CHANGED):
  428.  
  429.             log.debug("Fax address book has changed")
  430.             self.updateAddressBook()
  431.  
  432.         elif action == FAB_NAME_REMOVE:
  433.             log.debug("Fax address book has changed: '%s' removed" % s1)
  434.             all_names = self.db.get_all_names()
  435.             self.recipient_list = filter(lambda x: x in self.recipient_list, all_names)
  436.             self.updateAddressBook()
  437.             self.updateRecipientTable()
  438.  
  439.         elif action == FAB_NAME_RENAME:
  440.             log.debug("Fax address book has changed: '%s' renamed to '%s'" % (s1, s2))
  441.             for i, n in enumerate(self.recipient_list):
  442.                 if n == s1:
  443.                     self.recipient_list[i] = s2
  444.                     self.updateRecipientTable()
  445.                     break
  446.             else:
  447.                 self.updateAddressBook()
  448.  
  449.         elif action == FAB_NAME_DETAILS_CHANGED:
  450.             log.debug("Fax address book has changed: '%s' details changed" % s1)
  451.             self.updateRecipientTable()
  452.  
  453.  
  454.     def displayRecipientsPage(self):
  455.         self.updateAddressBook()
  456.         self.updateRecipientTable()
  457.         self.enableQuickAddButton()
  458.         self.displayPage(PAGE_RECIPIENTS)
  459.         self.restoreNextButton()
  460.         self.BackButton.setEnabled(True)
  461.  
  462.  
  463.     def updateAddressBook(self):
  464.         names = [n for n in self.db.get_all_names() if not n.startswith('__')]
  465.         groups = self.db.get_all_groups()
  466.         self.AddIndividualComboBox.clear()
  467.         self.AddGroupComboBox.clear()
  468.  
  469.         i = 0
  470.         names.sort()
  471.         for n in names:
  472.             if n not in self.recipient_list:
  473.                 self.AddIndividualComboBox.addItem(n)
  474.                 i += 1
  475.  
  476.         if i:
  477.             self.AddIndividualButton.setEnabled(True)
  478.             self.AddIndividualComboBox.setEnabled(True)
  479.             #self.AddIndividualButton.setIcon(QIcon(load_pixmap("add_user", "16x16")))
  480.  
  481.         else:
  482.             self.AddIndividualButton.setEnabled(False)
  483.             self.AddIndividualComboBox.setEnabled(False)
  484.             #self.AddIndividualButton.setIcon(QIcon(load_pixmap("add_user-disabled", "16x16")))
  485.  
  486.         i = 0
  487.         groups.sort()
  488.         for g in groups:
  489.             for n in self.db.group_members(g):
  490.                 if not n.startswith('__') and n not in self.recipient_list:
  491.                     self.AddGroupComboBox.addItem(g)
  492.                     i += 1
  493.                     break
  494.  
  495.         if i:
  496.             self.AddGroupButton.setEnabled(True)
  497.             self.AddGroupComboBox.setEnabled(True)
  498.             #self.AddGroupButton.setIcon(QIcon(load_pixmap("add_users", "16x16")))
  499.  
  500.         else:
  501.             self.AddGroupButton.setEnabled(False)
  502.             self.AddGroupComboBox.setEnabled(False)
  503.             #self.AddGroupButton.setIcon(QIcon(load_pixmap("add_users-disabled", "16x16")))
  504.  
  505.  
  506.     def updateRecipientTable(self):
  507.         try:
  508.             prev = self.getCurrentRecipient()
  509.         except (TypeError, AttributeError):
  510.             prev = None
  511.  
  512.         self.RecipientsTable.clear()
  513.         self.RecipientsTable.setRowCount(0)
  514.         self.RecipientsTable.setColumnCount(0)
  515.  
  516.         if self.recipient_list:
  517.             num_recipients = len(self.recipient_list)
  518.  
  519.             self.RecipientsTable.setColumnCount(len(self.recipient_headers))
  520.             self.RecipientsTable.setHorizontalHeaderLabels(self.recipient_headers)
  521.             self.RecipientsTable.setRowCount(num_recipients)
  522.             flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
  523.  
  524.             j = None
  525.             for row, n in enumerate(self.recipient_list):
  526.                 i = QTableWidgetItem(QString(n))
  527.                 i.setFlags(flags)
  528.                 self.RecipientsTable.setItem(row, 0, i)
  529.                 if prev is not None and n == prev:
  530.                     j = i
  531.  
  532.                 k = self.db.get(n)
  533.                 if not k:
  534.                     continue
  535.  
  536.                 i = QTableWidgetItem(QString(k['fax']))
  537.                 i.setFlags(flags)
  538.                 self.RecipientsTable.setItem(row, 1, i)
  539.  
  540.                 i = QTableWidgetItem(QString(k['notes']))
  541.                 i.setFlags(flags)
  542.                 self.RecipientsTable.setItem(row, 2, i)
  543.  
  544.             self.RecipientsTable.resizeColumnsToContents()
  545.             self.RecipientsTable.resizeRowsToContents()
  546.  
  547.             if j is not None:
  548.                 self.RecipientsTable.setCurrentItem(j)
  549.             else:
  550.                 self.RecipientsTable.setCurrentItem(self.RecipientsTable.item(0, 0))
  551.  
  552.             self.NextButton.setEnabled(True)
  553.  
  554.         else:
  555.             self.enableRecipientListButtons()
  556.             self.NextButton.setEnabled(False)
  557.  
  558.  
  559.     def RecipientsTable_itemSelectionChanged(self):
  560.         current_row = self.RecipientsTable.currentRow()
  561.         num_recipients = len(self.recipient_list)
  562.         self.enableRecipientListButtons(num_recipients > 0,  # remove
  563.                                             num_recipients > 1 and current_row > 0, # up
  564.                                             num_recipients > 1 and current_row < (num_recipients-1)) # down
  565.  
  566.  
  567.     def enableRecipientListButtons(self, enable_remove=False, enable_up_move=False, enable_down_move=False):
  568.         if enable_remove:
  569.             self.RemoveRecipientButton.setEnabled(True)
  570.         else:
  571.             self.RemoveRecipientButton.setEnabled(False)
  572.  
  573.         if enable_up_move:
  574.             self.MoveRecipientUpButton.setEnabled(True)
  575.         else:
  576.             self.MoveRecipientUpButton.setEnabled(False)
  577.  
  578.         if enable_down_move:
  579.             self.MoveRecipientDownButton.setEnabled(True)
  580.         else:
  581.             self.MoveRecipientDownButton.setEnabled(False)
  582.  
  583.  
  584.     def QuickAddFaxEdit_textChanged(self, fax):
  585.         self.enableQuickAddButton(None, unicode(fax))
  586.  
  587.  
  588.     def QuickAddNameEdit_textChanged(self, name):
  589.         self.enableQuickAddButton(unicode(name))
  590.  
  591.  
  592.     def enableQuickAddButton(self, name=None, fax=None):
  593.         if name is None:
  594.             name = unicode(self.QuickAddNameEdit.text())
  595.         if fax is None:
  596.             fax = unicode(self.QuickAddFaxEdit.text())
  597.  
  598.         existing_name = False
  599.         if name:
  600.             existing_name = name in self.db.get_all_names()
  601.  
  602.         if existing_name:
  603.             try:
  604.                 self.QuickAddNameEdit.setStyleSheet("background-color: yellow")
  605.             except AttributeError:
  606.                 pass
  607.         else:
  608.             try:
  609.                 self.QuickAddNameEdit.setStyleSheet("")
  610.             except AttributeError:
  611.                 pass
  612.  
  613.         if name and not existing_name and fax:
  614.             self.QuickAddButton.setEnabled(True)
  615.         else:
  616.             self.QuickAddButton.setEnabled(False)
  617.  
  618.  
  619.     def QuickAddButton_clicked(self):
  620.         name = unicode(self.QuickAddNameEdit.text())
  621.         fax = unicode(self.QuickAddFaxEdit.text())
  622.         self.fab.addName(name, fax)
  623.         self.addRecipient(name)
  624.         self.updateRecipientTable()
  625.         self.QuickAddNameEdit.clear()
  626.         self.QuickAddFaxEdit.clear()
  627.         self.enableQuickAddButton('', '')
  628.  
  629.  
  630.     def AddIndividualButton_clicked(self):
  631.         self.addRecipient(unicode(self.AddIndividualComboBox.currentText()))
  632.  
  633.  
  634.     def AddGroupButton_clicked(self):
  635.         self.addGroup(unicode(self.AddGroupComboBox.currentText()))
  636.  
  637.  
  638.     def RemoveRecipientButton_clicked(self):
  639.         name = self.getCurrentRecipient()
  640.         temp = self.recipient_list[:]
  641.         for i, n in enumerate(temp):
  642.             if name == n:
  643.                 del self.recipient_list[i]
  644.                 self.updateRecipientTable()
  645.                 self.updateAddressBook()
  646.                 break
  647.  
  648.  
  649.     def MoveRecipientUpButton_clicked(self):
  650.         utils.list_move_up(self.recipient_list, self.getCurrentRecipient())
  651.         self.updateRecipientTable()
  652.  
  653.  
  654.     def MoveRecipientDownButton_clicked(self):
  655.         utils.list_move_down(self.recipient_list, self.getCurrentRecipient())
  656.         self.updateRecipientTable()
  657.  
  658.  
  659.     def getCurrentRecipient(self):
  660.         item = self.RecipientsTable.item(self.RecipientsTable.currentRow(), 0)
  661.         if item is not None:
  662.             return unicode(item.text())
  663.         else:
  664.             return u''
  665.  
  666.  
  667.     def addRecipient(self, name, update=True):
  668.         if name not in self.recipient_list and not name.startswith('__'):
  669.             self.recipient_list.append(name)
  670.             if update:
  671.                 self.updateRecipientTable()
  672.                 self.updateAddressBook()
  673.  
  674.  
  675.     def addGroup(self, group):
  676.         for n in self.db.group_members(group):
  677.             self.addRecipient(n, False)
  678.  
  679.         self.updateRecipientTable()
  680.         self.updateAddressBook()
  681.  
  682.  
  683.     def FABButton_clicked(self):
  684.         self.fab.show()
  685.  
  686.  
  687.     def RecipientsTable_itemDoubleClicked(self, item):
  688.         if item is not None:
  689.             row, col = item.row(), item.column()
  690.             if col != 0:
  691.                 item = self.RecipientsTable.item(row, 0)
  692.  
  693.             self.fab.selectByName(unicode(item.text()))
  694.             self.fab.show()
  695.  
  696.  
  697.     #
  698.     # Send Fax Page
  699.     #
  700.  
  701.     def initSendFaxPage(self):
  702.         self.info_icon = QIcon(load_pixmap("info", "16x16"))
  703.         self.warn_icon = QIcon(load_pixmap("warning", "16x16"))
  704.         self.error_icon = QIcon(load_pixmap("error", "16x16"))
  705.         self.busy_icon = QIcon(load_pixmap("busy", "16x16"))
  706.         self.update_queue = Queue.Queue() # UI updates from send thread
  707.         self.event_queue = Queue.Queue() # UI events (cancel) to send thread
  708.         self.send_fax_active = False
  709.  
  710.  
  711.     def displaySendFaxPage(self):
  712.         self.displayPage(PAGE_SEND_FAX)
  713.         self.addStatusMessage(self.__tr("Ready to send fax."), self.info_icon)
  714.         self.NextButton.setText(self.__tr("Send Fax"))
  715.  
  716.  
  717.  
  718.     #
  719.     # Fax
  720.     #
  721.  
  722.     def executeSendFax(self):
  723.         self.NextButton.setEnabled(False)
  724.         self.BackButton.setEnabled(False)
  725.         self.CheckTimer.stop()
  726.         self.busy = True
  727.         phone_num_list = []
  728.  
  729.         ppd_file = cups.getPPD(self.printer_name)
  730.  
  731.         if ppd_file is not None and os.path.exists(ppd_file):
  732.             if file(ppd_file, 'r').read().find('HP Fax') == -1:
  733.                 FailureUI(self, self.__tr("<b>Fax configuration error.</b><p>The CUPS fax queue for '%1' is incorrectly configured.<p>Please make sure that the CUPS fax queue is configured with the 'HPLIP Fax' Model/Driver.").arg(self.printer_name))
  734.                 self.close()
  735.                 return
  736.  
  737.         beginWaitCursor()
  738.  
  739.         mq = device.queryModelByURI(self.device_uri)
  740.  
  741.         self.dev = fax.getFaxDevice(self.device_uri,
  742.                                    self.printer_name, None,
  743.                                    mq['fax-type'])
  744.  
  745.         try:
  746.             try:
  747.                 self.dev.open()
  748.             except Error, e:
  749.                 log.warn(e.msg)
  750.  
  751.             try:
  752.                 self.dev.queryDevice(quick=True)
  753.             except Error, e:
  754.                 log.error("Query device error (%s)." % e.msg)
  755.                 self.dev.error_state = ERROR_STATE_ERROR
  756.  
  757.         finally:
  758.             self.dev.close()
  759.             endWaitCursor()
  760.  
  761.         if self.dev.error_state > ERROR_STATE_MAX_OK and \
  762.             self.dev.error_state not in (ERROR_STATE_LOW_SUPPLIES, ERROR_STATE_LOW_PAPER):
  763.  
  764.             FailureUI(self, self.__tr("<b>Device is busy or in an error state (code=%1)</b><p>Please wait for the device to become idle or clear the error and try again.").arg(self.cur_device.status_code))
  765.             self.NextButton.setEnabled(True)
  766.             return
  767.  
  768.         # Check to make sure queue in CUPS is idle
  769.         self.cups_printers = cups.getPrinters()
  770.         for p in self.cups_printers:
  771.             if p.name == self.printer_name:
  772.                 if p.state == cups.IPP_PRINTER_STATE_STOPPED:
  773.                     FailureUI(self, self.__tr("<b>The CUPS queue for '%1' is in a stopped or busy state.</b><p>Please check the queue and try again.").arg(self.printer_name))
  774.                     self.NextButton.setEnabled(False)
  775.                     return
  776.                 break
  777.  
  778.         log.debug("Recipient list:")
  779.  
  780.         for p in self.recipient_list:
  781.             entry = self.db.get(p)
  782.             phone_num_list.append(entry)
  783.             log.debug("Name=%s Number=%s" % (entry["name"], entry["fax"]))
  784.  
  785.         log.debug("File list:")
  786.  
  787.         for f in self.file_list:
  788.             log.debug(f)
  789.  
  790.         self.dev.sendEvent(EVENT_START_FAX_JOB, self.printer_name, 0, '')
  791.  
  792.         if not self.dev.sendFaxes(phone_num_list, self.file_list, self.cover_page_message,
  793.                                   self.cover_page_re, self.cover_page_func, self.preserve_formatting,
  794.                                   self.printer_name, self.update_queue, self.event_queue):
  795.  
  796.             FailureUI(self, self.__tr("<b>Send fax is active.</b><p>Please wait for operation to complete."))
  797.             self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')
  798.             self.busy = False
  799.             self.send_fax_active = False
  800.             #self.NextButton.setEnabled(False)
  801.             self.setCancelCloseButton()
  802.             return
  803.  
  804.         self.send_fax_active = True
  805.         self.setCancelCloseButton()
  806.         self.SendFaxTimer = QTimer(self)
  807.         self.connect(self.SendFaxTimer, SIGNAL('timeout()'), self.SendFaxTimer_timeout)
  808.         self.SendFaxTimer.start(1000) # 1 sec UI updates
  809.  
  810.  
  811.     def setCancelCloseButton(self):
  812.         if self.send_fax_active:
  813.             self.CancelButton.setText(self.__tr("Cancel Send"))
  814.         else:
  815.             self.CancelButton.setText(self.__tr("Close"))
  816.  
  817.  
  818.     def CancelButton_clicked(self):
  819.         if self.send_fax_active:
  820.             self.addStatusMessage(self.__tr("Cancelling job..."), self.warn_icon)
  821.             self.event_queue.put((fax.EVENT_FAX_SEND_CANCELED, '', '', ''))
  822.             self.dev.sendEvent(EVENT_FAX_JOB_CANCELED, self.printer_name, 0, '')
  823.         else:
  824.             self.close()
  825.  
  826.  
  827.     def SendFaxTimer_timeout(self):
  828.         while self.update_queue.qsize():
  829.             try:
  830.                 status, page_num, arg = self.update_queue.get(0)
  831.             except Queue.Empty:
  832.                 break
  833.  
  834.             if status == fax.STATUS_IDLE:
  835.                 self.busy = False
  836.                 self.send_fax_active = False
  837.                 self.setCancelCloseButton()
  838.                 self.SendFaxTimer.stop()
  839.  
  840.             elif status == fax.STATUS_PROCESSING_FILES:
  841.                 self.addStatusMessage(self.__tr("Processing page %1...").arg(page_num), self.busy_icon)
  842.  
  843.             elif status == fax.STATUS_SENDING_TO_RECIPIENT:
  844.                 self.addStatusMessage(self.__tr("Sending fax to %1...").arg(arg), self.busy_icon)
  845.  
  846.             elif status == fax.STATUS_DIALING:
  847.                 self.addStatusMessage(self.__tr("Dialing %1...").arg(arg), self.busy_icon)
  848.  
  849.             elif status == fax.STATUS_CONNECTING:
  850.                 self.addStatusMessage(self.__tr("Connecting to %1...").arg(arg), self.busy_icon)
  851.  
  852.             elif status == fax.STATUS_SENDING:
  853.                 self.addStatusMessage(self.__tr("Sending page %1 to %2...").arg(page_num).arg(arg),
  854.                                       self.busy_icon)
  855.  
  856.             elif status == fax.STATUS_CLEANUP:
  857.                 self.addStatusMessage(self.__tr("Cleaning up..."), self.busy_icon)
  858.  
  859.             elif status in (fax.STATUS_ERROR, fax.STATUS_BUSY, fax.STATUS_COMPLETED):
  860.                 self.busy = False
  861.                 self.send_fax_active = False
  862.                 self.setCancelCloseButton()
  863.                 self.SendFaxTimer.stop()
  864.  
  865.                 if status  == fax.STATUS_ERROR:
  866.                     result_code, error_state = self.dev.getPML(pml.OID_FAX_DOWNLOAD_ERROR)
  867.                     #FailureUI(self, self.__tr("<b>Fax send error (%s).</b><p>" % pml.DN_ERROR_STR.get(error_state, "Unknown error")))
  868.                     self.addStatusMessage(self.__tr("Fax send error (%1)").arg(pml.DN_ERROR_STR.get(error_state, "Unknown error")), self.error_icon)
  869.                     self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')
  870.  
  871.                 elif status == fax.STATUS_BUSY:
  872.                     #FailureUI(self, self.__tr("<b>Fax device is busy.</b><p>Please try again later."))
  873.                     self.addStatusMessage(self.__tr("Fax is busy."), self.error_icon)
  874.                     self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')
  875.  
  876.                 elif status == fax.STATUS_COMPLETED:
  877.                     self.addStatusMessage(self.__tr("Send fax job complete."), self.info_icon)
  878.  
  879.                     self.dev.sendEvent(EVENT_END_FAX_JOB, self.printer_name, 0, '')
  880.  
  881.  
  882.     def addStatusMessage(self, text, icon):
  883.         log.debug(text)
  884.         #self.StatusList.addItem(QListWidgetItem(icon, text, self.StatusList))
  885.         QListWidgetItem(icon, text, self.StatusList)
  886.  
  887.     #
  888.     # CheckTimer and Fax Rendering
  889.     #
  890.  
  891.     def FileTable_callback(self, f):
  892.         # Called by FileTable when user adds a file using "Add file..."
  893.         log.debug("FileTable_callback(%s)" % f)
  894.         self.renderFile(f)
  895.  
  896.  
  897.     def renderFile(self, f):
  898.         self.busy = True
  899.         beginWaitCursor()
  900.         try:
  901.             self.last_job_id = cups.printFile(self.printer_name, f, os.path.basename(f))
  902.  
  903.         finally:
  904.             self.busy = False
  905.             #endWaitCursor()
  906.             #pass
  907.  
  908.  
  909.     def CheckTimer_timeout(self):
  910.         if not self.busy:
  911.             #log.debug("Checking for incoming faxes...")
  912.             device_uri, printer_name, event_code, username, job_id, title, timedate, fax_file = \
  913.                 self.service.CheckForWaitingFax(self.device_uri, prop.username, self.last_job_id)
  914.  
  915.             if fax_file:
  916.                 self.last_job_id = 0
  917.                 log.debug("A new fax has arrived: %s (%d)" % (fax_file, job_id))
  918.                 self.addFileFromJob(fax_file, title)
  919.  
  920.  
  921.     def addFileFromJob(self, fax_file, title):
  922.         self.busy = True
  923.         #beginWaitCursor()
  924.         try:
  925.             num_pages, hort_dpi, vert_dpi, page_size, resolution, encoding = \
  926.                 self.getFileInfo(fax_file)
  927.  
  928.             self.FilesTable.addFile(fax_file, 'application/hplip-fax', 'HPLIP Fax', title, num_pages)
  929.         finally:
  930.             self.busy = False
  931.             endWaitCursor()
  932.  
  933.  
  934.     def getFileInfo(self, fax_file):
  935.         f = file(fax_file, 'r')
  936.         header = f.read(fax.FILE_HEADER_SIZE)
  937.         f.close()
  938.  
  939.         if len(header) != fax.FILE_HEADER_SIZE:
  940.             log.error("Invalid fax file! (truncated header or no data)")
  941.             return (0, 0, 0, 0, 0, 0)
  942.  
  943.         mg, version, num_pages, hort_dpi, vert_dpi, page_size, \
  944.             resolution, encoding, reserved1, reserved2 = \
  945.             struct.unpack(">8sBIHHBBBII", header[:fax.FILE_HEADER_SIZE])
  946.  
  947.         log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
  948.                   (mg, version, num_pages, hort_dpi, vert_dpi, page_size, resolution, encoding))
  949.  
  950.         return (num_pages, hort_dpi, vert_dpi, page_size, resolution, encoding)
  951.  
  952.  
  953.     #
  954.     # Misc
  955.     #
  956.  
  957.     def closeEvent(self, e):
  958.         if self.lock_file is not None:
  959.             utils.unlock(self.lock_file)
  960.         e.accept()
  961.  
  962.  
  963.     def displayPage(self, page):
  964.         self.updateStepText(page)
  965.         self.StackedWidget.setCurrentIndex(page)
  966.  
  967.  
  968. #    def CancelButton_clicked(self):
  969. #        self.close()
  970.  
  971.  
  972.     def BackButton_clicked(self):
  973.         p = self.StackedWidget.currentIndex()
  974.         if p == PAGE_SELECT_FAX:
  975.             log.error("Invalid!")
  976.  
  977.         elif p == PAGE_COVERPAGE:
  978.             log.error("Invalid!")
  979.  
  980.         elif p == PAGE_FILES:
  981.             self.StackedWidget.setCurrentIndex(PAGE_COVERPAGE)
  982.             self.displayCoverpagePage()
  983.  
  984.         elif p == PAGE_RECIPIENTS:
  985.             self.StackedWidget.setCurrentIndex(PAGE_FILES)
  986.             self.displayFilesPage()
  987.  
  988.         elif p == PAGE_SEND_FAX:
  989.             self.StackedWidget.setCurrentIndex(PAGE_RECIPIENTS)
  990.             self.displayRecipientsPage()
  991.  
  992.  
  993.     def NextButton_clicked(self):
  994.         p = self.StackedWidget.currentIndex()
  995.         if p == PAGE_SELECT_FAX:
  996.             self.StackedWidget.setCurrentIndex(PAGE_COVERPAGE)
  997.             self.displayCoverpagePage()
  998.  
  999.         elif p == PAGE_COVERPAGE:
  1000.             self.StackedWidget.setCurrentIndex(PAGE_FILES)
  1001.             self.displayFilesPage()
  1002.  
  1003.         elif p == PAGE_FILES:
  1004.             self.StackedWidget.setCurrentIndex(PAGE_RECIPIENTS)
  1005.             self.displayRecipientsPage()
  1006.  
  1007.         elif p == PAGE_RECIPIENTS:
  1008.             self.StackedWidget.setCurrentIndex(PAGE_SEND_FAX)
  1009.             self.displaySendFaxPage()
  1010.  
  1011.         elif p == PAGE_SEND_FAX:
  1012.             self.executeSendFax()
  1013.  
  1014.  
  1015.     def updateStepText(self, p):
  1016.         self.StepText.setText(self.__tr("Step %1 of %2").arg(p+1).arg(PAGE_MAX+1))
  1017.  
  1018.  
  1019.     def restoreNextButton(self):
  1020.         self.NextButton.setText(self.__tr("Next >"))
  1021.  
  1022.  
  1023.     def __tr(self,s,c = None):
  1024.         return qApp.translate("SendFaxDialog",s,c)
  1025.  
  1026.  
  1027.